package com.autumn.zero.authorization.application.services.impl;

import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import com.autumn.common.api.domain.values.StringConstantItemValue;
import com.autumn.mybatis.criterion.EntityQuery;
import com.autumn.mybatis.criterion.EntitySpecifyUpdate;
import com.autumn.mybatis.criterion.LockModeEnum;
import com.autumn.mybatis.criterion.Query;
import com.autumn.util.AutoMapUtils;
import com.autumn.util.DataRollbackException;
import com.autumn.util.ExceptionUtils;
import com.autumn.util.StringUtils;
import com.autumn.zero.application.services.AbstractSimpleZeroEditAppService;
import com.autumn.zero.authorization.application.dto.modules.PermissionNameCheckInput;
import com.autumn.zero.authorization.application.dto.modules.ResourcesInput;
import com.autumn.zero.authorization.application.dto.modules.ResourcesModulePermissionDto;
import com.autumn.zero.authorization.application.dto.modules.ResourcesModuleTypeDto;
import com.autumn.zero.authorization.application.services.IResourcesModuleAppService;
import com.autumn.zero.authorization.application.services.IResourcesModulePermissionAppService;
import com.autumn.zero.authorization.constants.OperationPermissionConstants;
import com.autumn.zero.authorization.constants.ResourcesModuleConstants;
import com.autumn.zero.authorization.entitys.modules.ResourcesModule;
import com.autumn.zero.authorization.entitys.modules.ResourcesModulePermission;
import com.autumn.zero.authorization.entitys.modules.ResourcesModuleType;
import com.autumn.zero.authorization.entitys.roles.RolePermission;
import com.autumn.zero.authorization.entitys.users.UserPermission;
import com.autumn.zero.authorization.repositorys.modules.IResourcesModulePermissionRepository;
import com.autumn.zero.authorization.repositorys.modules.IResourcesModuleRepository;
import com.autumn.zero.authorization.repositorys.modules.IResourcesModuleTypeRepository;
import com.autumn.zero.authorization.repositorys.roles.IRolePermissionRepository;
import com.autumn.zero.authorization.repositorys.users.IUserPermissionRepository;
import com.autumn.zero.authorization.services.IResourcesService;
import com.autumn.zero.authorization.values.ResourcesModulePermissionTreeValue;
import com.autumn.zero.authorization.values.ResourcesModuleTreeValue;
import com.autumn.zero.authorization.values.ResourcesModuleValue;

/**
 * 资源模块应用服务
 * 
 * @author 老码农 2018-12-08 23:24:27
 */
public class ResourcesModuleAppServiceImpl extends
		AbstractSimpleZeroEditAppService<String, ResourcesModule, IResourcesModuleRepository, ResourcesModuleValue, ResourcesModuleValue>
		implements IResourcesModuleAppService {

	@Autowired
	protected IResourcesModuleRepository resourcesModuleRepository;

	@Autowired
	protected IUserPermissionRepository userPermissionRepository;

	@Autowired
	protected IRolePermissionRepository rolePermissionRepository;

	@Autowired
	protected IResourcesModulePermissionRepository resourcesModulePermissionRepository;

	@Autowired
	protected IResourcesService resourcesService;

	@Autowired
	protected IResourcesModulePermissionAppService resourcesModulePermissionAppService;

	@Autowired
	protected IResourcesModuleTypeRepository resourcesModuleTypeRepository;

	/**
	 * 
	 */
	@Override
	protected void queryByOrder(EntityQuery<ResourcesModule> query) {
		query.orderBy(ResourcesModule.FILED_PARENT_ID).orderBy(ResourcesModule.FILED_SORT_ID)
				.orderBy(ResourcesModule.FILED_ID);
	}

	private Set<String> searchMembers = new HashSet<>();

	@Override
	protected Set<String> getSearchMembers() {
		if (searchMembers.size() == 0) {
			searchMembers.add(ResourcesModule.FILED_NAME);
			searchMembers.add(ResourcesModule.FILED_CUSTOM_NAME);
		}
		return searchMembers;
	}

	/**
	 * 
	 */
	@Override
	protected IResourcesModuleRepository getRepository() {
		return this.resourcesModuleRepository;
	}

	private void editChedk(ResourcesModuleValue input, EntityQuery<ResourcesModule> query, boolean checkId,
			boolean checkName, boolean checkCustomName, boolean checkSort) {
		if (StringUtils.isNullOrBlank(input.getParentId())) {
			input.setParentId("");
		}
		if (input.getId().equalsIgnoreCase(input.getParentId())) {
			ExceptionUtils.throwValidationException("不能引用本身为父级。");
		}
		if (checkId) {
			query.reset();
			query.eq(ResourcesModule.FILED_ID, input.getId()).lock(LockModeEnum.UPDATE);
			if (query.countByWhere(this.resourcesModuleRepository) > 0) {
				ExceptionUtils.throwValidationException(this.getModuleName() + "id不能重复。");
			}
		}
		ResourcesModule parentModule = null;
		if (!StringUtils.isNullOrBlank(input.getParentId())) {
			parentModule = this.resourcesModuleRepository.get(input.getParentId());
			if (parentModule == null) {
				ExceptionUtils
						.throwValidationException("指定的父级" + this.getModuleName() + "[" + input.getParentId() + "]不存在。");
			}
			input.setResourcesType(parentModule.getResourcesType());
		} else {
			if (input.getResourcesType() == null) {
				ExceptionUtils.throwValidationException(this.getModuleName() + "类型不能为空。");
			}
			if (!this.existResourcesType(input.getResourcesType())) {
				ExceptionUtils.throwValidationException(this.getModuleName() + " 的资源类型不存在。");
			}
		}
		String msgName;
		if (StringUtils.isNullOrBlank(input.getParentId())) {
			msgName = "";
		} else {
			msgName = "同一父级下的";
		}
		if (checkName) {
			query.reset();
			query.eq(ResourcesModule.FILED_NAME, input.getName())
					.eq(ResourcesModule.FILED_RESOURCES_TYPE, input.getResourcesType())
					.eq(ResourcesModule.FILED_PARENT_ID, input.getParentId()).lock(LockModeEnum.UPDATE);
			if (query.countByWhere(this.resourcesModuleRepository) > 0) {
				ExceptionUtils
						.throwValidationException(msgName + this.getModuleName() + "名称[" + input.getName() + "]已重复。");
			}
		}
		if (StringUtils.isNullOrBlank(input.getCustomName())) {
			input.setCustomName(input.getName());
		} else {
			if (checkCustomName) {
				query.reset();
				query.eq(ResourcesModule.FILED_CUSTOM_NAME, input.getCustomName())
						.eq(ResourcesModule.FILED_RESOURCES_TYPE, input.getResourcesType())
						.eq(ResourcesModule.FILED_PARENT_ID, input.getParentId()).lock(LockModeEnum.UPDATE);
				if (query.countByWhere(this.resourcesModuleRepository) > 0) {
					ExceptionUtils.throwValidationException(
							msgName + "自定义" + this.getModuleName() + "名称[" + input.getCustomName() + "]已重复。");
				}
			}
		}
		if (checkSort) {
			query.reset();
			query.eq(ResourcesModule.FILED_SORT_ID, input.getSortId())
					.eq(ResourcesModule.FILED_RESOURCES_TYPE, input.getResourcesType())
					.eq(ResourcesModule.FILED_PARENT_ID, input.getParentId()).lock(LockModeEnum.UPDATE);
			if (query.countByWhere(this.resourcesModuleRepository) > 0) {
				ExceptionUtils.throwValidationException(msgName + "顺序[" + input.getSortId() + "]已重复。");
			}
		}
	}

	@Override
	protected ResourcesModule addBefore(ResourcesModuleValue input, EntityQuery<ResourcesModule> query) {
		if (input.getParentId() == null) {
			input.setParentId("");
		}
		this.editChedk(input, query, true, true, true, true);
		return super.addBefore(input, query);
	}

	@Override
	protected ResourcesModuleValue addAfter(ResourcesModuleValue input, ResourcesModule entity,
			EntityQuery<ResourcesModule> query) {
		resourcesService.clearModuleCache();
		return super.addAfter(input, entity, query);
	}

	@Override
	protected void updateBefore(ResourcesModuleValue input, ResourcesModule entity,
			EntityQuery<ResourcesModule> query) {
		if (input.getParentId() == null) {
			input.setParentId("");
		}
		boolean checkParent = !entity.getParentId().equalsIgnoreCase(input.getParentId());
		boolean checkSort = !entity.getSortId().equals(input.getSortId()) && checkParent;
		boolean checkName = !entity.getName().equalsIgnoreCase(input.getName()) && checkParent;
		boolean checkCustomName = !entity.getCustomName().equalsIgnoreCase(input.getCustomName()) && checkParent;
		this.editChedk(input, query, false, checkName, checkCustomName, checkSort);
		if (entity.getIsSysModule()) {
			input.setIsSysModule(true);
		}
		super.updateBefore(input, entity, query);
	}

	@Override
	protected ResourcesModuleValue updateAfter(ResourcesModuleValue input, ResourcesModule entity,
			ResourcesModule oldEntity, EntityQuery<ResourcesModule> query) {
		// 如果变更了资源类型
		if (!oldEntity.getResourcesType().equals(entity.getResourcesType())) {
			this.updateChildResourcesType(entity.getId(), entity.getResourcesType());
		}
		resourcesService.clearModuleCache();
		return super.updateAfter(input, entity, oldEntity, query);
	}

	/**
	 * 递归更新子级资源类型
	 * 
	 * @param resourcesType
	 *            资源类型
	 */
	private void updateChildResourcesType(String parentId, int resourcesType) {
		EntityQuery<ResourcesModule> query = new EntityQuery<>(ResourcesModule.class);
		query.eq(ResourcesModule.FILED_PARENT_ID, parentId);
		List<ResourcesModule> modules = query.selectByList(this.resourcesModuleRepository);
		if (modules.size() > 0) {
			EntitySpecifyUpdate<ResourcesModule> esu = new EntitySpecifyUpdate<>(ResourcesModule.class);
			esu.eq(ResourcesModule.FILED_PARENT_ID, parentId).set(ResourcesModule.FILED_RESOURCES_TYPE, resourcesType);
			esu.update(this.resourcesModuleRepository);
			for (ResourcesModule module : modules) {
				this.updateChildResourcesType(module.getId(), resourcesType);
			}
		}
	}

	@Override
	protected void deleteBefore(String id) {
		super.deleteBefore(id);
		ResourcesModule module = this.resourcesModuleRepository.get(id);
		if (module == null) {
			return;
		}
		if (module.getIsSysModule()) {
			ExceptionUtils.throwValidationException(module.getName() + " 属系统模块，不能删除。");
		}

		// 父级引用判断
		Query query = new Query(this.getEntityClass());
		query.eq(ResourcesModule.FILED_PARENT_ID, id).lock(LockModeEnum.UPDATE);
		if (this.resourcesModuleRepository.countByWhere(query.builderSection()) > 0) {
			ExceptionUtils.throwValidationException(this.getModuleName() + "[" + id + "]已有子级不能删除。");
		}

		// 删除用户权限引用的资源
		query = new Query(UserPermission.class);
		query.eq(UserPermission.FILED_RESOURCES_ID, id);
		this.userPermissionRepository.deleteByWhere(query.builderSection());

		// 删除角色权限引用的资源
		query = new Query(RolePermission.class);
		query.eq(RolePermission.FILED_RESOURCES_ID, id);
		this.rolePermissionRepository.deleteByWhere(query.builderSection());

		// 删除模块权限引用的资源
		query = new Query(ResourcesModulePermission.class);
		query.eq(ResourcesModulePermission.FILED_RESOURCES_ID, id);
		this.resourcesModulePermissionRepository.deleteByWhere(query.builderSection());
	}

	@Override
	protected void deleteAfter(String id, boolean isSoftDelete) {
		resourcesService.clearModuleCache();
		super.deleteAfter(id, isSoftDelete);
	}

	@Override
	@Transactional(rollbackFor = DataRollbackException.class)
	public boolean addDefaultSystemModule() {
		EntityQuery<ResourcesModule> query = new EntityQuery<>(ResourcesModule.class);
		boolean isAdd = true;
		Collection<ResourcesModule> items = ResourcesModuleConstants.items();
		for (ResourcesModule item : items) {
			if (item.getParentId() == null) {
				item.setParentId("");
			}
			query.reset();
			query.eq(ResourcesModule.FILED_ID, item.getId()).lock(LockModeEnum.UPDATE);
			if (query.countByWhere(this.getRepository()) > 0) {
				isAdd = false;
				break;
			}
			query.reset();
			query.eq(ResourcesModule.FILED_NAME, item.getName()).eq(ResourcesModule.FILED_PARENT_ID, item.getParentId())
					.lock(LockModeEnum.UPDATE);
			if (query.countByWhere(this.getRepository()) > 0) {
				isAdd = false;
				break;
			}
		}
		if (isAdd) {
			for (ResourcesModule item : items) {
				ResourcesModule module = new ResourcesModule();
				AutoMapUtils.map(item, module);
				module.forNullToDefault();
				this.getRepository().insert(module);
			}
			addDefaultPermission();
			resourcesService.clearModuleCache();
			return true;
		}
		return false;
	}

	/**
	 * 添加默认权限
	 */
	private void addDefaultPermission() {
		// 添加默认权限配置
		ResourcesInput input;
		input = new ResourcesInput(ResourcesModuleConstants.SYS_PERMISSION_ROLE);
		resourcesModulePermissionAppService.addDefaultEditPermission(input);
		input = new ResourcesInput(ResourcesModuleConstants.SYS_PERMISSION_USER);
		resourcesModulePermissionAppService.addDefaultEditPermission(input);

		input = new ResourcesInput(ResourcesModuleConstants.SYS_LOGIN_ATTEMPT);
		resourcesModulePermissionAppService.addDefaultQueryPermission(input);
		this.addClearPermission(ResourcesModuleConstants.SYS_LOGIN_ATTEMPT);

		input = new ResourcesInput(ResourcesModuleConstants.SYS_LOGIN_LOG);
		resourcesModulePermissionAppService.addDefaultQueryPermission(input);
		this.addClearPermission(ResourcesModuleConstants.SYS_LOGIN_LOG);

		input = new ResourcesInput(ResourcesModuleConstants.SYS_MODULE_RESOURCES);
		resourcesModulePermissionAppService.addDefaultEditPermission(input);
	}

	private void addClearPermission(String resourcesId) {
		PermissionNameCheckInput nameInput = new PermissionNameCheckInput();
		nameInput.setResourcesId(resourcesId);
		nameInput.setName(OperationPermissionConstants.CLEAR);
		if (resourcesModulePermissionAppService.existPermissionName(nameInput)) {
			return;
		}
		StringConstantItemValue item = OperationPermissionConstants.getItem(OperationPermissionConstants.CLEAR);
		ResourcesModulePermissionDto res = new ResourcesModulePermissionDto();
		res.setResourcesId(resourcesId);
		res.setFriendlyName(item.getName());
		res.setName(item.getValue());
		res.setPermissionUrl("/deleteAll");
		res.setSummary(item.getExplain());
		res.setSortId(102);
		resourcesModulePermissionAppService.add(res);
	}

	@Override
	public List<ResourcesModuleTreeValue> queryByTree() {
		return resourcesService.queryByTree();
	}

	@Override
	public List<ResourcesModuleTreeValue> queryByTree(int resourcesType) {
		return resourcesService.queryByTree(resourcesType);
	}

	@Override
	public List<ResourcesModulePermissionTreeValue> queryByPermissionTree() {
		return resourcesService.queryByPermissionTree();
	}

	@Override
	public List<ResourcesModulePermissionTreeValue> queryByPermissionTree(int resourcesType) {
		return resourcesService.queryByPermissionTree(resourcesType);
	}

	@Override
	public List<ResourcesModuleTreeValue> queryByMenuTree(int resourcesType) {
		return resourcesService.queryByMenuTree(resourcesType);
	}

	@Override
	public String getModuleName() {
		return "资源";
	}

	@Override
	public List<ResourcesModuleTypeDto> queryResourcesTypeList() {
		EntityQuery<ResourcesModuleType> query = new EntityQuery<>(ResourcesModuleType.class);
		query.orderBy(ResourcesModuleType.FILED_ID);
		List<ResourcesModuleType> entitys = query.selectByList(this.resourcesModuleTypeRepository);
		return AutoMapUtils.mapList(entitys, ResourcesModuleTypeDto.class);
	}

	@Override
	public ResourcesModuleTypeDto queryResourcesTypeById(long id) {
		ResourcesModuleType entity = this.resourcesModuleTypeRepository.get(id);
		return AutoMapUtils.map(entity, ResourcesModuleTypeDto.class);
	}

	@Override
	public boolean existResourcesType(long id) {
		EntityQuery<ResourcesModuleType> query = new EntityQuery<>(ResourcesModuleType.class);
		query.eq(ResourcesModuleType.FILED_ID, id);
		return query.exist(this.resourcesModuleTypeRepository);
	}
}
